"""
定义了所有与“工具”(Tool)资源相关的 API 端点。

使用 FastAPI 的 APIRouter 来组织路由。
"""
from fastapi import APIRouter, Depends, HTTPException, Query
from fastapi.responses import PlainTextResponse
from sqlalchemy.orm import Session
from typing import List, Optional
from uuid import UUID
import httpx
import logging

from db import crud
from schemas import tool as tool_schemas
from db.session import get_db
from core.config import MCP_LINK_BASE_URL, MANAGEMENT_API_HOST_URL, MCP_LINK_HOST_URL # Import all configs
from urllib.parse import quote_plus # Import for URL encoding

# 配置日志
logger = logging.getLogger(__name__)

router = APIRouter()

@router.post("/tools/", response_model=tool_schemas.ToolInDB)
def create_tool_endpoint(tool: tool_schemas.ToolCreate, db: Session = Depends(get_db)):
    """
    创建新工具。

    接收一个包含工具信息的请求体，并在数据库中创建一个新记录。
    """
    return crud.create_tool(db=db, tool=tool)

@router.get("/tools/", response_model=List[tool_schemas.ToolInDB])
def read_tools_endpoint(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
    """
    获取工具列表。

    支持使用 `skip` 和 `limit` 参数进行分页。
    """
    tools = crud.get_tools(db, skip=skip, limit=limit)
    return tools

@router.get("/tools/{tool_id}", response_model=tool_schemas.ToolInDB)
def read_tool_endpoint(tool_id: UUID, db: Session = Depends(get_db)):
    """
    根据 ID 获取单个工具的详细信息。
    """
    db_tool = crud.get_tool(db, tool_id=tool_id)
    if db_tool is None:
        raise HTTPException(status_code=404, detail="Tool not found")
    return db_tool

@router.put("/tools/{tool_id}", response_model=tool_schemas.ToolInDB)
def update_tool_endpoint(tool_id: UUID, tool: tool_schemas.ToolUpdate, db: Session = Depends(get_db)):
    """
    根据 ID 更新一个现有工具的信息。
    """
    db_tool = crud.update_tool(db=db, tool_id=tool_id, tool=tool)
    if db_tool is None:
        raise HTTPException(status_code=404, detail="Tool not found")
    return db_tool

@router.delete("/tools/{tool_id}", response_model=tool_schemas.ToolInDB)
def delete_tool_endpoint(tool_id: UUID, db: Session = Depends(get_db)):
    """
    根据 ID 删除一个工具。
    """
    db_tool = crud.delete_tool(db=db, tool_id=tool_id)
    if db_tool is None:
        raise HTTPException(status_code=404, detail="Tool not found")
    return db_tool

@router.get("/tools/{tool_id}/spec", response_class=PlainTextResponse)
def get_tool_spec_endpoint(tool_id: UUID, db: Session = Depends(get_db)):
    """
    获取指定工具的原始 OpenAPI 规范内容。

    此端点主要用于给 mcp-link 引擎提供规范的 URL。
    """
    db_tool = crud.get_tool(db, tool_id=tool_id)
    if db_tool is None:
        raise HTTPException(status_code=404, detail="Tool not found")
    return db_tool.openapi_spec_content

@router.get("/tools/{tool_id}/mcp-url")
def get_tool_mcp_url_endpoint(tool_id: UUID, db: Session = Depends(get_db)):
    """
    为指定工具动态生成一个可供 mcp-link 引擎直接使用的 URL。
    """
    db_tool = crud.get_tool(db, tool_id=tool_id)
    if db_tool is None:
        raise HTTPException(status_code=404, detail="Tool not found")
    
    # 重要：解决Docker网络边界问题
    # - 外部AI客户端访问：使用 localhost:5001（宿主机地址）
    # - MCP-Link内部访问：使用 host.docker.internal:5001（容器访问宿主机）
    
    # 对于 mcp-link 内部访问 management-api，使用 Docker 宿主机访问方式
    mcp_link_accessible_api_url = "http://host.docker.internal:5001"
    
    # 生成 mcp-link 内部使用的 spec URL（容器访问宿主机）
    spec_url_for_mcp_link = f"{mcp_link_accessible_api_url}/api/v1/tools/{tool_id}/spec"
    
    # 生成供外部（主机）使用的 MCP URL
    # s 参数：mcp-link 内部访问的 spec URL（容器访问宿主机）
    # u 参数：外部 API 的 base URL
    # t 参数：工具ID，用于MCP-Link保存MCP技术名称映射
    # 整个 URL：外部客户端访问的 mcp-link 地址
    mcp_url = (
        f"{MCP_LINK_HOST_URL}/sse?"
        f"s={quote_plus(spec_url_for_mcp_link)}&"  # s 参数使用容器访问宿主机的地址
        f"u={quote_plus(db_tool.target_api_url)}&"  # u 参数是外部 API 的 base URL
        f"t={tool_id}"  # t 参数是工具ID
    )
    
    return {"mcp_url": mcp_url}

@router.get("/logs")
async def get_logs_endpoint(
    tool: Optional[str] = Query(None, description="按工具名称过滤日志"),
    level: Optional[str] = Query(None, description="按日志级别过滤 (DEBUG, INFO, WARN, ERROR)"),
    limit: int = Query(50, ge=1, le=500, description="返回的日志数量限制")
):
    """
    获取 MCP-Link 服务的日志信息。
    
    这个接口作为代理，统一转发对 MCP-Link 日志端点的请求。
    这样可以实现：
    1. 统一的 API 入口
    2. 集中的安全控制
    3. 一致的错误处理
    4. 更好的监控和日志
    """
    try:
        # 构建 MCP-Link 内部日志 URL（Docker 内部网络）
        mcp_link_logs_url = "http://mcp-link:8080/logs"
        
        # 构建查询参数
        params = {}
        if tool:
            params["tool"] = tool
        if level:
            params["level"] = level.upper()  # 确保级别为大写
        params["limit"] = limit
        
        # 记录请求日志
        logger.info(f"代理日志请求: tool={tool}, level={level}, limit={limit}")
        
        # 使用 httpx 异步请求 MCP-Link 日志接口
        async with httpx.AsyncClient(timeout=10.0) as client:
            response = await client.get(mcp_link_logs_url, params=params)
            
            # 检查响应状态
            if response.status_code == 200:
                logs_data = response.json()
                logger.info(f"成功获取日志: {logs_data.get('total', 0)} 条记录")
                return logs_data
            else:
                logger.error(f"MCP-Link 日志接口错误: {response.status_code} - {response.text}")
                raise HTTPException(
                    status_code=response.status_code,
                    detail=f"MCP-Link 服务错误: {response.text}"
                )
                
    except httpx.TimeoutException:
        logger.error("请求 MCP-Link 日志接口超时")
        raise HTTPException(
            status_code=504,
            detail="MCP-Link 服务响应超时"
        )
    except httpx.RequestError as e:
        logger.error(f"请求 MCP-Link 日志接口失败: {e}")
        raise HTTPException(
            status_code=503,
            detail="无法连接到 MCP-Link 服务"
        )
    except Exception as e:
        logger.error(f"获取日志时发生未知错误: {e}")
        raise HTTPException(
            status_code=500,
            detail="内部服务器错误"
        )


# 添加获取MCP技术名称映射的端点
@router.get("/tools/{tool_id}/mcp-names")
def get_tool_mcp_names_endpoint(tool_id: UUID, db: Session = Depends(get_db)):
    """
    获取指定工具的MCP技术名称映射。
    """
    db_tool = crud.get_tool(db, tool_id=tool_id)
    if db_tool is None:
        raise HTTPException(status_code=404, detail="Tool not found")
    
    # 确保返回空对象而不是None
    mcp_tool_names = db_tool.mcp_tool_names if db_tool.mcp_tool_names is not None else {}
    
    return {"mcp_tool_names": mcp_tool_names}


# 添加更新MCP技术名称映射的端点
@router.put("/tools/{tool_id}/mcp-names")
def update_tool_mcp_names_endpoint(
    tool_id: UUID, 
    mcp_names: dict,
    db: Session = Depends(get_db)
):
    """
    更新指定工具的MCP技术名称映射。
    """
    db_tool = crud.get_tool(db, tool_id=tool_id)
    if db_tool is None:
        raise HTTPException(status_code=404, detail="Tool not found")
    
    # 更新MCP技术名称映射
    mcp_tool_names = mcp_names.get("mcp_tool_names", mcp_names)
    tool_update = tool_schemas.ToolUpdate(mcp_tool_names=mcp_tool_names)
    updated_tool = crud.update_tool(db=db, tool_id=tool_id, tool=tool_update)
    
    if updated_tool is None:
        raise HTTPException(status_code=404, detail="Tool not found")
    
    return {"mcp_tool_names": updated_tool.mcp_tool_names}
